import http from 'http/serverRequests'
import axios from 'axios'
import type { AxiosRequestConfig, CancelToken } from 'axios'
import moduleStore from '../../store'
import { Store } from 'vuex'
import Vue from 'vue'

type GetStorateFunc = <T extends string>(name: NonEmptyString<T>) => any

type SetStorateFunc = <T extends string>(name: NonEmptyString<T>, value: any) => void

type ResponseAwaitFunc<T> = {
    status: 'succ',
    data: T
} | {
    status: 'fail',
    reason: any
}

const scopeName = process.env.APP_SCOPE_NAME

let store!: Store<any>

if (Vue.prototype.isPortal) {
    store = window[window.poolName].store
} else {
    store = moduleStore as Store<any>
}

// 获取带组织名和模块名的localStorage
const getLocalStorage: GetStorateFunc = (name) => {
    if (!name) throw new Error('name of getLocalStorage is required')
    let result = localStorage.getItem(`${store.state.organization ? `${store.state.organization}-` : ''}${scopeName}-${name}`)
    return result ? JSON.parse(result) : result
}

// 设置带组织名和模块名的localStorage
const setLocalStorage: SetStorateFunc = (name, value) => {
    if (!name) throw new Error('name of setLocalStorage is required')
    return localStorage.setItem(`${store.state.organization ? `${store.state.organization}-` : ''}${scopeName}-${name}`, JSON.stringify(value))
}

// 判断数据是否为对象
function isObject (value: any): boolean {
    let result: string = Object.prototype.toString.call(value)
    return result === '[object Object]'
}

function createQueryStringUrl (url: string, obj: {
    [index: string]: string | number
}) {
    if (typeof obj !== 'object' || obj === null) return url
    let keys = Object.keys(obj)
    if (keys.length) {
        url = url + '?'
    }
    keys.map((k, i, arr) => {
        url = url + k + '=' + obj[k] + (i === arr.length - 1 ? '' : '&')
    })
    return url
}

async function awaitFunc<R> (likePromise: Promise<R>) {
    let res!: ResponseAwaitFunc<R>
    await new Promise<void>((resolve) => {
        likePromise.then(r => {
            res = {
                status: 'succ',
                data: r
            }
        }).catch(e => {
            res = {
                status: 'fail',
                reason: e
            }
        }).finally(() => {
            resolve()
        })
    })
    return res
}

/**
 * @function 创建http请求辅助函数
 * @param token axios的取消token
 * @param mode http模式，多模块或者是单一模块
 */
function createHttpHelp (arg: {
    token?: CancelToken,
    mode?: 'model' | 'single'
}) {
    /**
     * @function http请求promise化
     * @param param 请求的参数，特别注意：若没有参数并且需要传config，param需要传undefined而不是null
     * @param config 请求的配置，其中cancelToken若不传会被token覆盖（若token存在）
     * @return Proxy
     * 使用时: this.http.xx.xx<T> 建议传入泛型，项目赶时间可不传，默认为any
     */

    let { token, mode = 'single' } = arg

    if (mode !== 'single' && mode !== 'model') {
        mode = 'single'
    }

    let httpFunc!: Function

    function httpHelp<T = any> (param?: any, config?: AxiosRequestConfig): Promise<T> {
        if (config && !config.cancelToken && token) {
            config.cancelToken = token
        } else if (!config && token) {
            config = {
                cancelToken: token
            }
        }

        return new Promise((resolve, reject) => {
            httpFunc(...[...(param !== undefined ? [param] : []), res => {
                if (Object.prototype.toString.call(res.data) === '[object Blob]') {
                    const fileReader = new FileReader()
                    fileReader.onload = () => {
                        try {
                            const result = fileReader.result
                            if (typeof result === 'string') {
                                let json = JSON.parse(result)
                                if (json.code !== 'fail') {
                                    resolve(json.data)
                                } else {
                                    reject(json.info)
                                }
                                return
                            }
                            throw new Error()
                        } catch {
                            resolve(res)
                        }
                    }
                    fileReader.readAsText(res.data)
                } else {
                    if (res.code !== 'fail') {
                        resolve(res.data)
                    } else {
                        reject(res.info)
                    }
                }
            }, err => {
                if (!axios.isCancel(err)) {
                    reject(err)
                }
            }, config || {}])
        })
    }

    const getFunc = cacheHttp => (target, key) => {
        if (mode === 'single') {
            httpFunc = http[key]
            return httpHelp
        } else {
            if (isObject(cacheHttp[key])) {
                return new Proxy({}, {
                    get: getFunc(cacheHttp[key])
                })
            } else {
                httpFunc = cacheHttp[key]
                return httpHelp
            }
        }
    }
    return new Proxy({}, {
        get: getFunc(http)
    })
}

export {
    createQueryStringUrl,
    isObject,
    createHttpHelp,
    setLocalStorage,
    getLocalStorage,
    awaitFunc
}
